﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

using Peradigm.Framework.Domain.Exceptions;
using Peradigm.Framework.Domain.Security;
using Peradigm.Framework.Persistence;
using Peradigm.Framework.Persistence.Exceptions;

namespace Liberty.Persistence.Security
{
    public class UserBroker : CachingBroker
    {
        #region Constants
        private const string
            SP_SAVE_USER = "Security.SaveUser",
            SP_ALL_USERS = "Security.AllUsers";

        private const string
            FIELD_OID = "Oid",
            FIELD_GUID = "Guid",
            FIELD_FULL_NAME = "FullName",
            FIELD_UPN = "Upn",
            FIELD_LAST_SIGNED_IN = "LastSignedIn",
            FIELD_UPDATED_DATE = "UpdatedDate";

        private const string
            CACHE_ALL_USERS = "UserBroker.AllUsers";

        private const string
            FIELD_PREFIX = "@";

        private const string
            ERROR_SAVE_USER = "Application failed to save user to the database: {0}.",
            ERR_READ_ALL_USERS = "Application failed to retrieve users from the database: {0}. ";
        #endregion

        #region Methods
        public List<User> AllUsers()
        {
            List<User> allUsers = this.Cache.Find( CACHE_ALL_USERS ) as List<User>;
            if ( allUsers == null )
            {
                lock ( this.Cache )
                {
                    allUsers = this.Cache.Find( CACHE_ALL_USERS ) as List<User>;
                    if ( allUsers == null )
                    {
                        allUsers = this.PrimAllUsers();
                        this.CacheItem( CACHE_ALL_USERS, allUsers );
                        this.RegisterKey( CACHE_ALL_USERS );
                    }
                }
            }
            return new List<User>( allUsers );
        }
        public User UserWith( Guid userGuid )
        {
            return this.AllUsers().Find( user => user.Guid == userGuid );
        }
        public User UserWith( long oid )
        {
            return this.AllUsers().Find( user => user.Oid == oid );
        }
        public User SaveUser( User user )
        {
            User dbUser = this.UserWith( user.Guid );
            user.Oid = ( dbUser != null ) ? dbUser.Oid : User.NEW_OID;
            user.Version = ( dbUser != null ) ? dbUser.Version : User.NEW_VERSION;
            user = this.PrimSaveUser( user );
            if ( dbUser == null )
            {
                this.RemoveCacheItem( CACHE_ALL_USERS );
            }
            return user;
        }
        #endregion

        #region Properties
        #endregion

        #region Private Methods
        private User PrimSaveUser( User user )
        {
            SqlCommand cmd = null;
            SafeReader reader = null;

            try
            {
                cmd = this.CommandFor( SP_SAVE_USER );

                cmd.Parameters.Add( new SqlParameter( FIELD_PREFIX + FIELD_OID, user.Oid ) );
                cmd.Parameters.Add( new SqlParameter( FIELD_PREFIX + FIELD_GUID, user.Guid ) );
                cmd.Parameters.Add( new SqlParameter( FIELD_PREFIX + FIELD_FULL_NAME, user.Name ) );
                cmd.Parameters.Add( new SqlParameter( FIELD_PREFIX + FIELD_UPN, user.UPN ) );
                
                reader = this.ExecuteReader( cmd );
                reader.Read();
                user.Oid = reader.GetInt64( FIELD_OID );
            }
            catch ( Exception e )
            {
                string msg = String.Format( ERROR_SAVE_USER, e.Message );
                throw new PersistenceException( msg, e, Severity.Catastrophic );
            }
            finally
            {
                this.Close( reader );
                this.Close( cmd );
            }

            return user;
        }
        
        private List<User> PrimAllUsers()
        {
            List<User> allUsers = new List<User>();
            SqlCommand cmd = null;
            SafeReader reader = null;

            try
            {
                cmd = this.CommandFor( SP_ALL_USERS );
                reader = this.ExecuteReader( cmd );
                while( reader.Read() )
                {
                    allUsers.Add( this.UserFrom( reader ) );
                }
            }
            catch ( Exception e )
            {
                string msg = String.Format( ERR_READ_ALL_USERS, e.Message );
                throw new PersistenceException( msg, e, Severity.Catastrophic );
            }
            finally
            {
                this.Close( reader );
                this.Close( cmd );
            }
            return allUsers;
        }
        private User UserFrom( SafeReader reader )
        {
            long oid = reader.GetInt64( FIELD_OID );
            string fullName = reader.GetString( FIELD_FULL_NAME );
            string upn = reader.GetString( FIELD_UPN );
            string guidValue = reader.GetString( FIELD_GUID );
            Guid guid = ( this.IsValidGuid( guidValue ) ) ? new Guid( guidValue ) : new Guid();
            User user = new User( oid, fullName );
            user.Guid = guid;
            user.UPN = upn;
            return user;
        }
        private bool IsValidGuid( string expression )
        {
            Regex guidRegEx = new Regex( @"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$" );
            return guidRegEx.IsMatch( expression );
        }
        #endregion

        #region Private Properties
        #endregion

        #region Construction and Finalization
        public UserBroker( SqlTransaction txn )
            : base( txn )
        {
        }

        public UserBroker( string cxnString )
            : base( cxnString )
        {
        }
        #endregion

        #region Data Elements
        #endregion
    }
}
